/*
 * Created on Feb 11, 2005
 * Created by dfhuynh
 */
package edu.mit.simile.longwell.query.bucket;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.TreeSet;

import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;

import edu.mit.simile.longwell.Profile;
import edu.mit.simile.longwell.query.project.IProjection;
import edu.mit.simile.longwell.schema.ISchemaModel;

/**
 * @author dfhuynh
 */
public class DistinctValueBucketer extends BucketerBase {
	/**
	 * @param profile
	 */
	public DistinctValueBucketer(Profile profile) {
		super(profile);
	}
	
	/* (non-Javadoc)
	 * @see edu.mit.simile.longwell.query.bucket.BucketerBase#internalGetBucket(edu.mit.simile.longwell.query.project.IProjection, java.lang.String)
	 */
	protected Set internalGetBucket(IProjection projection, String parameter) {
		String[] params = splitParameter(parameter);
		Set objects = new HashSet();
		
		for (int i = 0; i < params.length; i++) {
			objects.addAll(projection.getObjects(parameterToNode(params[i])));
		}
		return objects;
	}

	/* (non-Javadoc)
	 * @see edu.mit.simile.longwell.query.bucket.BucketerBase#internalSuggestBuckets(edu.mit.simile.longwell.query.project.IProjection, desirable)
	 */
	protected List internalSuggestNarrowingBuckets(IProjection projection, float desirable) {
		ISchemaModel 	schemaModel = m_profile.getSchemaModel();
		String			locale = projection.getLocale();
		List 			bucketThemes = new ArrayList();
		
		if (desirable < 1 &&
			(projection.getUniqueness() * desirable > 0.5f ||
			 projection.countObjects((Object) null) > projection.countObjects() / 3)) {
			return bucketThemes;
		}
		
		Set values = projection.getValues();
		if (desirable < 1 && values.size() == 1) {
			return bucketThemes;
		}
		
		int totalCount = projection.countObjects();
		float valuesToObjectsRatio = values.size() / (float) totalCount;
		
		if (desirable == 1 || valuesToObjectsRatio < 0.5f * desirable) { // TODO 0.5f is a magic number for now
			Set buckets = new TreeSet(new Bucket.BucketComparator());
			
			Iterator i = values.iterator();
			while (i.hasNext()) {
				Object value = i.next();
				
				int count = projection.countObjects(value);
				if (count > 0 && count < totalCount) {
					buckets.add(
						new Bucket(
							this.getClass().getName(), 
							encodeParameter(valueToParameter(value, schemaModel, locale)),
							valueToLabel(value, schemaModel, locale),
							count));
				}
			}
			
			if (buckets.size() > 0) {
				bucketThemes.add(new BucketTheme(
					"",
					"distinct-values",
					new ArrayList(buckets)
				));
			}
		}
		
		return bucketThemes;
	}

	/* (non-Javadoc)
	 * @see edu.mit.simile.longwell.query.bucket.BucketerBase#internalSuggestBroadeningBuckets(edu.mit.simile.longwell.query.project.IProjection, java.lang.String)
	 */
	protected BroadeningResult internalSuggestBroadeningBuckets(
			IProjection projection, String parameter) {
		
		ISchemaModel	schemaModel = m_profile.getSchemaModel();
		String			locale = projection.getLocale();
		String[] 		params = splitParameter(parameter);
		
		List existingBuckets = new ArrayList();
		for (int i = 0; i < params.length; i++) {
			String param = "";
			for (int j = 0; j < params.length; j++) {
				if (i != j) {
					if (param.length() == 0) {
						param += encodeParameter(params[j]);
					} else {
						param += "," + encodeParameter(params[j]);
					}
				}
			}
			
			existingBuckets.add(
				new Bucket(
					this.getClass().getName(), 
					param,
					valueToLabel(
						parameterToNode(params[i]), 
						schemaModel, 
						locale
					),
					0));
		}
		
		List bucketThemes = new ArrayList();
		{
			Set buckets = new TreeSet(new Bucket.BucketComparator());
			Set values = projection.getValues();

			Iterator i = values.iterator();
			outer:while (i.hasNext()) {
				Object value = i.next();
				String param = valueToParameter(value, schemaModel, locale);
				
				for (int j = 0; j < params.length; j++) {
					if (param.equals(params[j])) {
						continue outer;
					}
				}
				
				buckets.add(
					new Bucket(
						this.getClass().getName(), 
						parameter + "," + encodeParameter(param),
						valueToLabel(value, schemaModel, locale),
						0));
			}
			
			if (buckets.size() > 0) {
				bucketThemes.add(new BucketTheme(
					"",
					"distinct-values",
					new ArrayList(buckets)
				));
			}
		}
		
		return new BroadeningResult(existingBuckets, bucketThemes);
	}
	
	/* (non-Javadoc)
	 * @see edu.mit.simile.longwell.query.bucket.BucketerBase#individualParameterToDescription(java.lang.String, java.lang.String)
	 */
	protected String individualParameterToDescription(String parameter,
			String locale) {
		
		return valueToLabel(
			parameterToNode(parameter), m_profile.getSchemaModel(), locale);
	}
	
	protected String valueToParameter(Object object, ISchemaModel schemaModel, String locale) {
		if (object instanceof Literal) {
			String s = ((Literal) object).getString();
			
			return "l" + s;
		} else if (object instanceof String) {
			return "l" + (String) object;
		} else if (object instanceof Resource) {
			return "r" + 
				schemaModel.resourceToPseudoURI((Resource) object);
		} else {
			return "null";
		}
	}
	
	protected String valueToLabel(Object object, ISchemaModel schemaModel, String locale) {
		ResourceBundle 	resources = getResources(locale);
		
		String literalBucketLabelFormat = 
			resources.getString("LiteralBucketLabelFormat");
		String resourceBucketLabelFormat = 
			resources.getString("ResourceBucketLabelFormat");
		String emptyBucketLabel = 
			resources.getString("EmptyBucketLabel");
		
		if (object instanceof Literal) {
			String s = ((Literal) object).getString();
			
			return MessageFormat.format(
				literalBucketLabelFormat, new Object[] {s});
		} else if (object instanceof Resource) {
			return MessageFormat.format(
				resourceBucketLabelFormat, new Object[] {
					schemaModel.getLabel((Resource) object, locale, true)
				}
			);
		} else if (object instanceof String) {
			return (String) object;
		} else {
			return emptyBucketLabel;
		}
	}
	
	protected RDFNode parameterToNode(String s) {
		RDFNode node = null;
		if (s.startsWith("l")) {
			node = m_profile.getModel().createLiteral(s.substring(1));
		} else if (s.startsWith("r")) {
			node = m_profile.getSchemaModel().
				pseudoURIToResource(s.substring(1));
		}
		return node;
	}
}
